package com.woniuxy.yoga.user.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.io.file.FileNameUtil;
import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.woniuxy.yoga.commons.core.exception.YogaException;
import com.woniuxy.yoga.commons.core.model.PageInfo;
import com.woniuxy.yoga.commons.core.model.Result;
import com.woniuxy.yoga.commons.core.statedata.BusinessState;
import com.woniuxy.yoga.commons.core.statedata.CoachState;
import com.woniuxy.yoga.commons.core.statedata.UserState;
import com.woniuxy.yoga.commons.idcard.model.ResponseEntity;
import com.woniuxy.yoga.commons.idcard.util.AliCheckIdCard;
import com.woniuxy.yoga.commons.oss.util.OSSUtil;
import com.woniuxy.yoga.commons.redis.util.RedisUtil;
import com.woniuxy.yoga.commons.redis.util.rediskeyutil.UserRedisKeyUtil;
import com.woniuxy.yoga.user.client.BusinessClient;
import com.woniuxy.yoga.user.client.CoachClient;
import com.woniuxy.yoga.user.dao.model.UserInfo;
import com.woniuxy.yoga.user.dao.mapper.UserInfoMapper;
import com.woniuxy.yoga.user.model.dto.*;
import com.woniuxy.yoga.user.model.exception.UserException;
import com.woniuxy.yoga.user.model.exception.UserExceptionCode;
import com.woniuxy.yoga.user.model.param.*;
import com.woniuxy.yoga.user.service.UserInfoService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.validation.Valid;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * <p>
 * 用户信息表 服务实现类
 * </p>
 *
 * @author 韦用文,刘存益,郎腾霄
 * @since 2023年11月28日
 */
@Service
public class UserInfoServiceImpl extends ServiceImpl<UserInfoMapper, UserInfo> implements UserInfoService {

    @Autowired
    private UserInfoMapper userInfoMapper;

    @Autowired
    private RedisUtil redisUtil;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    private RedissonClient redissonClient;

    @Autowired
    private AliCheckIdCard aliCheckIdCard;

    @Autowired
    private OSSUtil ossUtil;

    @Autowired
    private CoachClient coachClient;

    @Autowired
    private BusinessClient businessClient;


    /**
     * 发送用户注册短信
     * @param param param
     */
    @Override
    public void sendUserRegCode(SendUserRegCodeParam param) {
        QueryWrapper<UserInfo> wrapper = new QueryWrapper<>();
        wrapper.eq("user_phone",param.getUserPhone());
        UserInfo userInfo = userInfoMapper.selectOne(wrapper);
        if (userInfo!=null) throw new UserException(UserExceptionCode.USER_EXCEPTION_CODE_PHONE_OCCUPY);
        if (redisUtil.hasKey(UserRedisKeyUtil.userRegisterCodeKey(param.getUserPhone()))) throw new UserException(UserExceptionCode.USER_EXCEPTION_CODE_SMSCODEINVALID);
        String numberString = redisUtil.get(UserRedisKeyUtil.userSendNumbers(param.getUserPhone()));
        if (numberString!=null){
            if (Integer.parseInt(numberString)==5){
                throw new UserException(UserExceptionCode.USER_EXCEPTION_CODE_SEND_CODE_IN_HOUR);
            }
        }
        //将手机号码保存到userRegister.sendCode中
        HashMap<String, Object> message = new HashMap<>();
        message.put("phone",param.getUserPhone());
        rabbitTemplate.convertAndSend("userRegister.sendCode",message);
    }

    /**
     * 用户注册
     * @param param param
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void userRegister(UserRegisterParam param) throws Exception {
        //验证验证码是否有误
        String code = redisUtil.get(UserRedisKeyUtil.userRegisterCodeKey(param.getUserPhone()));
        if (code==null || !code.equals(param.getCode())) throw new UserException(UserExceptionCode.USER_EXCEPTION_CODE_CODE_EXPIRE);
        //创建多资源锁
        RLock userNameLock = redissonClient.getFairLock(UserRedisKeyUtil.lockRegisterByUserName(param.getUserName()));
        RLock userPhoneLock = redissonClient.getFairLock(UserRedisKeyUtil.lockRegisterByUserPhone(param.getUserPhone()));
        RLock lock = redissonClient.getMultiLock(userNameLock, userPhoneLock);
        try {
            //加锁，如果获取成功则返回true，同时启动看门机制。获取失败则最多重试10s,然后返回false。
            boolean isLock=lock.tryLock(15, TimeUnit.SECONDS);
            if (!isLock) throw new YogaException(500,"服务器繁忙，请稍后再试");
            //查询用户名是否重复
            QueryWrapper<UserInfo> wrapper = new QueryWrapper<>();
            wrapper.eq("user_name",param.getUserName());
            UserInfo userInfo = userInfoMapper.selectOne(wrapper);
            if (userInfo!=null) throw new UserException(UserExceptionCode.USER_EXCEPTION_CODE_NAME_OCCUPY);
            //查询手机号
            QueryWrapper<UserInfo> wrapper1 = new QueryWrapper<>();
            wrapper1.eq("user_phone",param.getUserPhone());
            UserInfo userInfo1 = userInfoMapper.selectOne(wrapper1);
            if (userInfo1!=null) throw new UserException(UserExceptionCode.USER_EXCEPTION_CODE_PHONE_OCCUPY);
            UserInfo info = BeanUtil.toBean(param, UserInfo.class);
            info.setUserState(UserState.USER_ACCOUNT_STATE_NO_AUTHENTICATION);
            //通过雪花算法生成随机字符串拼接成用户昵称
            String nickId = new Snowflake(1, 1).nextIdStr().substring(0,8);
            info.setUserNickName("用户"+nickId);
            //默认头像
            info.setUserAvatar("https://weiyongwen.oss-cn-chengdu.aliyuncs.com/0774fc065d094f7bbc190eef0af50648.jpg");
            userInfoMapper.insert(info);
            //删除redis中的code
            redisUtil.delete(redisUtil.get(UserRedisKeyUtil.userRegisterCodeKey(param.getUserPhone())));
        }finally {
            //释放锁
            if (userNameLock.isLocked()&&userPhoneLock.isLocked()){
                lock.unlock();
            }
        }
    }

    /**
     * 发送用户登录验证码
     * @param param param
     */
    @Override
    public void sendUserLogCode(SendUserLogCodeParam param) {
        //判断手机号是否存在
        QueryWrapper<UserInfo> wrapper = new QueryWrapper<>();
        wrapper.eq("user_phone",param.getUserPhone());
        wrapper.ne("user_state",UserState.USER_ACCOUNT_STATE_DELETE);
        UserInfo userInfo = userInfoMapper.selectOne(wrapper);
        if (userInfo==null) throw new UserException(UserExceptionCode.USER_EXCEPTION_CODE_NAME_NOT_EXIST);
        if (Objects.equals(userInfo.getUserState(), UserState.USER_ACCOUNT_STATE_DISABLE))
            throw new UserException(UserExceptionCode.USER_EXCEPTION_CODE_STATE_NOT_NORMAL);
        //判断一分钟内是否发送两次验证码
        if (redisUtil.hasKey(UserRedisKeyUtil.userLoginCodeOnMinute(param.getUserPhone())))
            throw new UserException(UserExceptionCode.USER_EXCEPTION_CODE_SMSCODEINVALID);
        //判断一小时内是否已获取五次验证码
        String numberString = redisUtil.get(UserRedisKeyUtil.userSendNumbers(param.getUserPhone()));
        if (numberString!=null){
            if (Integer.parseInt(numberString)==5)
                throw new UserException(UserExceptionCode.USER_EXCEPTION_CODE_SEND_CODE_IN_HOUR);
        }
        //将手机号保存到userLogin.sendCode中
        HashMap<String, Object> message = new HashMap<>();
        message.put("phone",param.getUserPhone());
        rabbitTemplate.convertAndSend("userLogin.sendCode",message);
    }

    /**
     * 用户验证码登录
     * @param param param
     * @return UserInfoDto
     */
    @Override
    public UserInfoDto loginByCode(LoginByCodeParam param) {
        //判断验证码
        String code = redisUtil.get(UserRedisKeyUtil.userLoginCodeKey(param.getUserPhone()));
        if (code == null || !param.getCode().equals(code))
            throw new UserException(UserExceptionCode.USER_EXCEPTION_CODE_CODE_EXPIRE);
        QueryWrapper<UserInfo> wrapper = new QueryWrapper<>();
        wrapper.eq("user_phone",param.getUserPhone());
        wrapper.ne("user_state",UserState.USER_ACCOUNT_STATE_DELETE);
        UserInfo userInfo = userInfoMapper.selectOne(wrapper);
        if (userInfo==null) throw new UserException(UserExceptionCode.USER_EXCEPTION_CODE_NAME_NOT_EXIST);
        if (Objects.equals(userInfo.getUserState(), UserState.USER_ACCOUNT_STATE_DISABLE))
            throw new UserException(UserExceptionCode.USER_EXCEPTION_CODE_STATE_NOT_NORMAL);
        //创建登录日志
        loginLog(userInfo,param.getIp());
        //返回登录对象
        return BeanUtil.toBean(userInfo, UserInfoDto.class);
    }

    /**
     * 创建登录日志
     * @param userInfo 用户对象
     * @param ip ip地址
     */
    public void loginLog(UserInfo userInfo,String ip){
        HashMap<String, Object> message = new HashMap<>();
        message.put("userId",userInfo.getUserId());
        message.put("userName",userInfo.getUserName());
        message.put("userPhone",userInfo.getUserPhone());
        message.put("loginTime", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        message.put("ip",ip);
        //创建消息队列
        rabbitTemplate.convertAndSend("user.loginLog",message);
    }

    /**
     * 用户密码登录
     * @param param 参数
     * @return UserInfoDto
     */
    @Override
    public UserInfoDto loginByPass(LoginByPassParam param) {
        QueryWrapper<UserInfo> wrapper = new QueryWrapper<>();
        wrapper.eq("user_name",param.getAccount()).or().eq("user_phone",param.getAccount());
        wrapper.ne("user_state",UserState.USER_ACCOUNT_STATE_DELETE);
        UserInfo userInfo = userInfoMapper.selectOne(wrapper);
        if (userInfo==null) throw new UserException(UserExceptionCode.USER_EXCEPTION_CODE_NAME_NOT_EXIST);
        //判断密码
        if (!userInfo.getUserPass().equals(param.getUserPass())) throw new UserException(UserExceptionCode.USER_EXCEPTION_CODE_PASS_ERROR);
        if (Objects.equals(userInfo.getUserState(), UserState.USER_ACCOUNT_STATE_DISABLE))
            throw new UserException(UserExceptionCode.USER_EXCEPTION_CODE_STATE_NOT_NORMAL);
        //创建登录日志
        loginLog(userInfo,param.getIp());
        //返回登录对象
        return BeanUtil.toBean(userInfo, UserInfoDto.class);
    }

    /**
     * 禁用用户账号
     * @param param 参数
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void disableUser(ChangeUserStateParam param) {
        UserInfo userInfo = userInfoMapper.selectById(param.getUserId());
        if (userInfo==null) throw new UserException(UserExceptionCode.USER_EXCEPTION_CODE_NAME_NOT_EXIST);
        //查看是否为禁用或删除状态
        if (!Objects.equals(userInfo.getUserState(), UserState.USER_ACCOUNT_STATE_NO_AUTHENTICATION) && !Objects.equals(userInfo.getUserState(), UserState.USER_ACCOUNT_STATE_NORMAL) && !Objects.equals(userInfo.getUserState(), UserState.USER_ACCOUNT_STATE_AUTHENTICATION_FAILED))
            throw new UserException(UserExceptionCode.USER_EXCEPTION_CODE_STATE_NOT_NORMAL);
        //禁用账号
        userInfo.setUserState(UserState.USER_ACCOUNT_STATE_DISABLE);
        userInfoMapper.updateById(userInfo);
    }

    /**
     * 启用用户账号
     * @param param param
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void enableUser(ChangeUserStateParam param) {
        UserInfo userInfo = userInfoMapper.selectById(param.getUserId());
        if (userInfo==null) throw new UserException(UserExceptionCode.USER_EXCEPTION_CODE_NAME_NOT_EXIST);
        //查看是否为正常状态
        if (!Objects.equals(userInfo.getUserState(), UserState.USER_ACCOUNT_STATE_DISABLE))
            throw new UserException(UserExceptionCode.USER_EXCEPTION_CODE_STATE_NOT_NORMAL);
        //启用账号
        if (ObjectUtil.isNotEmpty(userInfo.getIdCard())&&ObjectUtil.isNotEmpty(userInfo.getRealName()))
            userInfo.setUserState(UserState.USER_ACCOUNT_STATE_NORMAL);
        else userInfo.setUserState(UserState.USER_ACCOUNT_STATE_NO_AUTHENTICATION);
        userInfoMapper.updateById(userInfo);
    }

    /**
     * 删除用户账号
     * @param param 参数
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delUser(ChangeUserStateParam param) {
        UserInfo userInfo = userInfoMapper.selectById(param.getUserId());
        if (userInfo==null) throw new UserException(UserExceptionCode.USER_EXCEPTION_CODE_NAME_NOT_EXIST);
        //查看是否已经是删除状态
        if (Objects.equals(userInfo.getUserState(), UserState.USER_ACCOUNT_STATE_DELETE))
            throw new UserException(UserExceptionCode.USER_EXCEPTION_CODE_STATE_NOT_NORMAL);
        //删除账号
        userInfo.setUserState(UserState.USER_ACCOUNT_STATE_DELETE);
        userInfoMapper.updateById(userInfo);
    }

    /**
     * 用户修改头像
     * @param param 参数
     * @return 字符串
     * @throws IOException IOException
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public String updateUserAvatar(UpdateUserAvatarParam param) throws IOException {
        //查询用户信息
        UserInfo userInfo = userInfoMapper.selectById(param.getUserId());
        if (userInfo==null) throw new UserException(UserExceptionCode.USER_EXCEPTION_CODE_NAME_NOT_EXIST);
        if (Objects.equals(userInfo.getUserState(), UserState.USER_ACCOUNT_STATE_DISABLE) || Objects.equals(userInfo.getUserState(), UserState.USER_ACCOUNT_STATE_DELETE))
            throw new UserException(UserExceptionCode.USER_EXCEPTION_CODE_STATE_NOT_NORMAL);
        String newPath = ossUtil.uploadFile(param.getImage().getInputStream(), param.getFileName());
        if (userInfo.getUserAvatar()!=null && !userInfo.getUserAvatar().equals("")){
            String path = FileNameUtil.getName(userInfo.getUserAvatar());
            ossUtil.deleteFile("liucunyi",path);
        }
        //修改数据库
        userInfo.setUserAvatar(newPath);
        userInfoMapper.updateById(userInfo);
        return newPath;
    }

    /**
     * 分页查询用户
     * @param param 参数
     * @return 分页数据
     */
    @Override
    public PageInfo findUsers(ListUsersParam param) {
        if (ObjectUtil.isEmpty(param.getPageNum()))
            param.setPageNum(1);
        if (ObjectUtil.isEmpty(param.getPageSize()))
            param.setPageSize(10);
        //创建查询
        QueryWrapper<UserInfo> wrapper = new QueryWrapper<>();
        if (ObjectUtil.isNotEmpty(param.getUserName()))
            wrapper.like("user_name",param.getUserName());
        if (ObjectUtil.isNotEmpty(param.getUserPhone()))
            wrapper.like("user_phone",param.getUserPhone());
        if (ObjectUtil.isNotEmpty(param.getIdCard()))
            wrapper.like("id_card",param.getIdCard());
        if (ObjectUtil.isNotEmpty(param.getUserState()))
            wrapper.like("user_state",param.getUserState());
        wrapper.orderByDesc("user_id");
        //创建分页对象
        Page<UserInfo> page = new Page<>(param.getPageNum(),param.getPageSize());
        userInfoMapper.selectPage(page,wrapper);
        PageInfo pageInfo = BeanUtil.toBean(page, PageInfo.class);
        pageInfo.setPages(page.getPages());
        pageInfo.setRecords(page.getRecords());
        return pageInfo;
    }

    /**
     * 用户修改基本信息
     * @param param 参数
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateUserInfo(UpdateUserInfoParam param) {
        UserInfo userInfo = userInfoMapper.selectById(param.getUserId());
        if (userInfo==null) throw new UserException(UserExceptionCode.USER_EXCEPTION_CODE_NAME_NOT_EXIST);
        if (Objects.equals(userInfo.getUserState(), UserState.USER_ACCOUNT_STATE_DISABLE) || Objects.equals(userInfo.getUserState(), UserState.USER_ACCOUNT_STATE_DELETE))
            throw new UserException(UserExceptionCode.USER_EXCEPTION_CODE_STATE_NOT_NORMAL);
        //修改信息
        userInfo.setUserNickName(param.getUserNickName());
        userInfo.setUserEmail(param.getUserEmail());
        userInfoMapper.updateById(userInfo);
    }

    /**
     * 修改密码
     * @param param 参数
     */
    @Override
    @Transactional
    public void changePwd(ChangePwdParam param) {
        //判断新旧密码是否一致
        if (param.getNewPass().equals(param.getOdlPass()))
            throw new UserException(UserExceptionCode.USER_EXCEPTION_CODE_NEW_OLD_PASS);
        //查询账号
        UserInfo userInfo = userInfoMapper.selectById(param.getUserId());
        if (userInfo==null) throw new UserException(UserExceptionCode.USER_EXCEPTION_CODE_NAME_NOT_EXIST);
        if (Objects.equals(userInfo.getUserState(), UserState.USER_ACCOUNT_STATE_DISABLE) || Objects.equals(userInfo.getUserState(), UserState.USER_ACCOUNT_STATE_DELETE))
            throw new UserException(UserExceptionCode.USER_EXCEPTION_CODE_STATE_NOT_NORMAL);
        //判断旧密码是否正确
        if (!userInfo.getUserPass().equals(param.getOdlPass()))
            throw new UserException(UserExceptionCode.USER_EXCEPTION_CODE_OLD_PASS_ERROR);
        userInfo.setUserPass(param.getNewPass());
        userInfoMapper.updateById(userInfo);
    }

    /**
     * 实名认证
     * @param param 参数
     * @throws Exception Exception
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void identify(IdentifyParam param) throws Exception {
        //查询账号
        UserInfo userInfo = userInfoMapper.selectById(param.getUserId());
        if (userInfo==null) throw new UserException(UserExceptionCode.USER_EXCEPTION_CODE_NAME_NOT_EXIST);
        if (Objects.equals(userInfo.getUserState(), UserState.USER_ACCOUNT_STATE_DISABLE) || Objects.equals(userInfo.getUserState(), UserState.USER_ACCOUNT_STATE_DELETE))
            throw new UserException(UserExceptionCode.USER_EXCEPTION_CODE_STATE_NOT_NORMAL);
        if (Objects.equals(userInfo.getUserState(), UserState.USER_ACCOUNT_STATE_NORMAL))
            throw new UserException(UserExceptionCode.USER_EXCEPTION_CODE_IDENTIFY_SUCCESS);
        //调用实名认证接口
        ResponseEntity response = aliCheckIdCard.checkIdCard(param.getRealName(), param.getIdCard());
        //判断认证是否成功
        //认证失败
        if ((!"10000".equals(response.getCode())) || response.getData().getResult()!=1){
            userInfo.setUserState(UserState.USER_ACCOUNT_STATE_AUTHENTICATION_FAILED);
            userInfoMapper.updateById(userInfo);
            throw new UserException(UserExceptionCode.USER_EXCEPTION_CODE_IDENTIFY_FAIL);
        }
        //认证成功
        userInfo.setUserState(UserState.USER_ACCOUNT_STATE_NORMAL);
        userInfo.setRealName(param.getRealName());
        userInfo.setIdCard(param.getIdCard());
        userInfoMapper.updateById(userInfo);
    }

    /**
     * 发送修改手机号验证码
     * @param param 参数
     */
    @Override
    public void sendChangePhoneCode(SendUserChangePhoneCodeParam param) {
        QueryWrapper<UserInfo> wrapper = new QueryWrapper<>();
        wrapper.eq("user_phone",param.getUserPhone());
        UserInfo userInfo = userInfoMapper.selectOne(wrapper);
        if (userInfo!=null) throw new UserException(UserExceptionCode.USER_EXCEPTION_CODE_PHONE_OCCUPY);
        if (redisUtil.hasKey(UserRedisKeyUtil.userChangePhoneCodeKey(param.getUserPhone()))) throw new UserException(UserExceptionCode.USER_EXCEPTION_CODE_SMSCODEINVALID);
        String numberString = redisUtil.get(UserRedisKeyUtil.userSendNumbers(param.getUserPhone()));
        if (numberString!=null){
            if (Integer.parseInt(numberString)==5){
                throw new UserException(UserExceptionCode.USER_EXCEPTION_CODE_SEND_CODE_IN_HOUR);
            }
        }
        //将手机号码保存到userChangePhone.sendCode中
        HashMap<String, Object> message = new HashMap<>();
        message.put("phone",param.getUserPhone());
        rabbitTemplate.convertAndSend("userChangePhone.sendCode",message);
    }

    /**
     * 修改手机号
     * @param param 参数
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void changePhone(ChangePhoneParam param) {
        //判断验证码是否正确
        String code = redisUtil.get(UserRedisKeyUtil.userChangePhoneCodeKey(param.getUserPhone()));
        if (code==null || !code.equals(param.getCode())) throw new UserException(UserExceptionCode.USER_EXCEPTION_CODE_CODE_EXPIRE);
        //查询账号
        UserInfo userInfo = userInfoMapper.selectById(param.getUserId());
        if (userInfo==null) throw new UserException(UserExceptionCode.USER_EXCEPTION_CODE_NAME_NOT_EXIST);
        if (Objects.equals(userInfo.getUserState(), UserState.USER_ACCOUNT_STATE_DISABLE) || Objects.equals(userInfo.getUserState(), UserState.USER_ACCOUNT_STATE_DELETE))
            throw new UserException(UserExceptionCode.USER_EXCEPTION_CODE_STATE_NOT_NORMAL);
        //判断手机号是否被占用
        QueryWrapper<UserInfo> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("user_phone",param.getUserPhone());
        queryWrapper.ne("user_state",UserState.USER_ACCOUNT_STATE_DELETE);
        UserInfo info = userInfoMapper.selectOne(queryWrapper);
        if (info!=null) throw new UserException(UserExceptionCode.USER_EXCEPTION_CODE_PHONE_OCCUPY);
        //修改手机号
        userInfo.setUserPhone(param.getUserPhone());
        userInfoMapper.updateById(userInfo);
    }

    /**
     * 用户关注教练
     * @param userId 用户ID
     * @param coachId 教练ID
     */
    @Override
    public void followCoach(Integer userId, Integer coachId) {
        Result<Coach> result = coachClient.getByCoachId(coachId);
        if (result.getCode()!=200) throw new UserException(result.getCode(),result.getMessage());
        Coach coach = result.getData();
        if (coach==null) throw new UserException(UserExceptionCode.USER_EXCEPTION_CODE_COACH_NOT_EXIST);
        //查询教练状态
        if (!Objects.equals(coach.getCoachState(), CoachState.COACH_STATE_NORMAL))
            throw new UserException(UserExceptionCode.USER_EXCEPTION_CODE_COACH_NOT_NORMAL);
        //判断是否关注过该教练
        if (redisUtil.zScore(UserRedisKeyUtil.followCoach(userId),coachId.toString())!=null)
            throw new UserException(UserExceptionCode.USER_EXCEPTION_CODE_SUB_COACH_EXIST);
        //redis中添加zset 键值对 使用当前的时间戳作为排序值
        redisUtil.zAdd(UserRedisKeyUtil.followCoach(userId),coachId.toString(),new Date().getTime());
        //使收藏排行榜中的收藏量+1
        redisUtil.zIncrBy(UserRedisKeyUtil.followCoachRank(), coachId.toString(), 1);
    }

    /**
     * 用户取关教练
     * @param userId 用户id
     * @param coachId 教练id
     */
    @Override
    public void cancelFollowCoach(Integer userId, Integer coachId) {
        Result<Coach> result = coachClient.getByCoachId(coachId);
        if (result.getCode()!=200) throw new UserException(result.getCode(),result.getMessage());
        Coach coach = result.getData();
        if (coach==null) throw new UserException(UserExceptionCode.USER_EXCEPTION_CODE_COACH_NOT_EXIST);
        //查询教练状态
        if (!Objects.equals(coach.getCoachState(), CoachState.COACH_STATE_NORMAL))
            throw new UserException(UserExceptionCode.USER_EXCEPTION_CODE_COACH_NOT_NORMAL);
        //判断是否关注过该教练
        if (redisUtil.zScore(UserRedisKeyUtil.followCoach(userId),coachId.toString())==null)
            throw new UserException(UserExceptionCode.USER_EXCEPTION_CODE_SUB_COACH_NO_EXIST);
        //删除该用户关注的教练的key
        redisUtil.zRem(UserRedisKeyUtil.followCoach(userId), coachId.toString());
        //判断该教练目前的关注量
        if (redisUtil.zScore(UserRedisKeyUtil.followCoachRank(), coachId.toString()) > 1) {
            redisUtil.zIncrBy(UserRedisKeyUtil.followCoachRank(), coachId.toString(), -1);
        } else {
            redisUtil.zRem(UserRedisKeyUtil.followCoachRank(), coachId.toString());
        }
    }

    /**
     * 分页查询教练关注列表
     * @param pageNum 当前页数
     * @param pageSize 每页条数
     * @param userId 用户id
     * @return 分页数据
     */
    @Override
    public PageInfo searchFollowCoachRank(Integer pageNum, Integer pageSize, Integer userId) {
        //查询总条数
        Long total = redisUtil.zSize(UserRedisKeyUtil.followCoach(userId));
        if (pageNum == null) {
            pageNum = 1;
        }
        if (pageSize == null) {
            pageSize = 5;
        }
        Long pages = total % pageSize == 0 ? total / pageSize : total / pageSize + 1;
        ArrayList<Coach> list = new ArrayList<>();
        Set<String> set = redisUtil.zRevRange(UserRedisKeyUtil.followCoach(userId), pageNum, pageSize);
        if (set != null) {
            for (String coachId : set) {
                Result<Coach> result = coachClient.getByCoachId(Integer.parseInt(coachId));
                if (result.getCode()!=200) throw new UserException(result.getCode(),result.getMessage());
                Coach coach = result.getData();
                list.add(coach);
            }
        }
        PageInfo pageInfo = new PageInfo();
        pageInfo.setRecords(list);
        pageInfo.setPages(pages);
        return pageInfo;
    }

    /**
     * 查询关注教练排行榜
     * @return List<FollowCoachRankDto>
     */
    @Override
    public List<FollowCoachRankDto> searchFollowCoachRankList() {
        Set<String> list = redisUtil.zRevRange(UserRedisKeyUtil.followCoachRank(), 1, 10);
        List<FollowCoachRankDto> followCoachRankDtos = new ArrayList<>();
        if (list!=null && list.size()>0){
            for (String coachId : list) {
                Result<Coach> result = coachClient.getByCoachId(Integer.parseInt(coachId));
                if (result.getCode()!=200) throw new UserException(result.getCode(),result.getMessage());
                Coach coach = result.getData();
                if (coach==null) throw new UserException(UserExceptionCode.USER_EXCEPTION_CODE_COACH_NOT_EXIST);
                //查询教练状态
                if (!Objects.equals(coach.getCoachState(), CoachState.COACH_STATE_NORMAL))
                    throw new UserException(UserExceptionCode.USER_EXCEPTION_CODE_COACH_NOT_NORMAL);
                Double score = redisUtil.zScore(UserRedisKeyUtil.followCoachRank(), coachId);
                FollowCoachRankDto followCoachRankDto = BeanUtil.toBean(coach, FollowCoachRankDto.class);
                followCoachRankDto.setScore(score);
                followCoachRankDtos.add(followCoachRankDto);
            }
        }
        return followCoachRankDtos;
    }

    /**
     * 教练分页查看被关注的用户信息
     * @param param 查询参数
     * @return PageInfo
     */
    @Override
    public PageInfo listCoachFollowedUsers(ListFollowedUsersParam param) {
        Result<Coach> result = coachClient.getByCoachId(param.getId());
        if (result.getCode()!=200) throw new UserException(result.getCode(),result.getMessage());
        //创建分页对象
        Page<UserInfoDto> page = new Page<>(param.getPageNum()==null||param.getPageNum().equals("")?1:param.getPageNum(),
                param.getPageSize()==null||param.getPageSize().equals("")?10:param.getPageSize());
        QueryWrapper<UserInfo> wrapper = new QueryWrapper<>();
        if (ObjectUtil.isNotEmpty(param.getUserName()))
            wrapper.like("user_name",param.getUserName());
        if (ObjectUtil.isNotEmpty(param.getUserPhone()))
            wrapper.like("user_phone",param.getUserPhone());
        wrapper.eq("user_state",UserState.USER_ACCOUNT_STATE_NORMAL);
        List<UserInfo> userInfos = userInfoMapper.selectList(wrapper);
        List<UserInfoDto> userInfoDtos = new ArrayList<>();
        for (UserInfo userInfo : userInfos) {
            if (redisUtil.hasKey(UserRedisKeyUtil.followCoach(userInfo.getUserId()))){
                UserInfoDto userInfoDto = BeanUtil.toBean(userInfo, UserInfoDto.class);
                userInfoDtos.add(userInfoDto);
            }
        }
        page.setRecords(userInfoDtos);
        PageInfo pageInfo = new PageInfo();
        pageInfo.setRecords(page.getRecords());
        pageInfo.setPages(page.getPages());
        return pageInfo;
    }

    /**
     * 用户关注场馆
     * @param userId 用户id
     * @param storeId 场馆id
     */
    @Override
    public void followStore(Integer userId, Integer storeId) {
        Result<BusinessStore> result = businessClient.selByStoreId(storeId);
        if (result.getCode()!=200) throw new UserException(result.getCode(),result.getMessage());
        BusinessStore businessStore = result.getData();
        if (businessStore==null) throw new UserException(UserExceptionCode.USER_EXCEPTION_CODE_STORE_NOT_EXIST);
        //查询场馆状态
        if (!Objects.equals(businessStore.getStoreState(), BusinessState.STORE_STATE_NORMAL))
            throw new UserException(UserExceptionCode.USER_EXCEPTION_CODE_STORE_NOT_NORMAL);
        //判断是否关注过该场馆
        if (redisUtil.zScore(UserRedisKeyUtil.followStore(userId),storeId.toString())!=null)
            throw new UserException(UserExceptionCode.USER_EXCEPTION_CODE_SUB_STORE_EXIST);
        //redis中添加zset 键值对 使用当前的时间戳作为排序值
        redisUtil.zAdd(UserRedisKeyUtil.followStore(userId),storeId.toString(),new Date().getTime());
        //使收藏排行榜中的收藏量+1
        redisUtil.zIncrBy(UserRedisKeyUtil.followStoreRank(), storeId.toString(), 1);
    }

    /**
     * 用户取消关注场馆
     * @param userId 用户id
     * @param storeId 场馆id
     */
    @Override
    public void cancelFollowStore(Integer userId, Integer storeId) {
        Result<BusinessStore> result = businessClient.selByStoreId(storeId);
        if (result.getCode()!=200) throw new UserException(result.getCode(),result.getMessage());
        BusinessStore businessStore = result.getData();
        if (businessStore==null) throw new UserException(UserExceptionCode.USER_EXCEPTION_CODE_STORE_NOT_EXIST);
        //查询场馆状态
        if (!Objects.equals(businessStore.getStoreState(), BusinessState.STORE_STATE_NORMAL))
            throw new UserException(UserExceptionCode.USER_EXCEPTION_CODE_STORE_NOT_NORMAL);
        //判断是否关注过该场馆
        if (redisUtil.zScore(UserRedisKeyUtil.followStore(userId),storeId.toString())==null)
            throw new UserException(UserExceptionCode.USER_EXCEPTION_CODE_SUB_STORE_NO_EXIST);
        //删除该用户关注的场馆的key
        redisUtil.zRem(UserRedisKeyUtil.followStore(userId), storeId.toString());
        //判断该场馆目前的关注量
        if (redisUtil.zScore(UserRedisKeyUtil.followStoreRank(), storeId.toString()) > 1) {
            redisUtil.zIncrBy(UserRedisKeyUtil.followStoreRank(), storeId.toString(), -1);
        } else {
            redisUtil.zRem(UserRedisKeyUtil.followStoreRank(), storeId.toString());
        }
    }

    /**
     * 分页查询关注场馆列表
     * @param pageNum 当前页数
     * @param pageSize 每页条数
     * @param userId 用户id
     * @return 分页数据
     */
    @Override
    public PageInfo searchFollowStoreRank(Integer pageNum, Integer pageSize, Integer userId) {
        //查询总条数
        Long total = redisUtil.zSize(UserRedisKeyUtil.followStore(userId));
        if (pageNum == null) {
            pageNum = 1;
        }
        if (pageSize == null) {
            pageSize = 5;
        }
        Long pages = total % pageSize == 0 ? total / pageSize : total / pageSize + 1;
        ArrayList<BusinessStore> list = new ArrayList<>();
        Set<String> set = redisUtil.zRevRange(UserRedisKeyUtil.followStore(userId), pageNum, pageSize);
        if (set != null) {
            for (String storeId : set) {
                Result<BusinessStore> result = businessClient.selByStoreId(Integer.parseInt(storeId));
                if (result.getCode()!=200) throw new UserException(result.getCode(),result.getMessage());
                BusinessStore businessStore = result.getData();
                list.add(businessStore);
            }
        }
        PageInfo pageInfo = new PageInfo();
        pageInfo.setRecords(list);
        pageInfo.setPages(pages);
        return pageInfo;
    }

    /**
     * 查询关注场馆排行榜
     * @return List<FollowStoreRankDto>
     */
    @Override
    public List<FollowStoreRankDto> searchFollowStoreRankList() {
        Set<String> list = redisUtil.zRevRange(UserRedisKeyUtil.followStoreRank(), 1, 10);
        List<FollowStoreRankDto> followStoreRankDtos = new ArrayList<>();
        if (list!=null && list.size()>0){
            for (String storeId : list) {
                Result<BusinessStore> result = businessClient.selByStoreId(Integer.parseInt(storeId));
                if (result.getCode()!=200) throw new UserException(result.getCode(),result.getMessage());
                BusinessStore businessStore = result.getData();
                if (businessStore==null) throw new UserException(UserExceptionCode.USER_EXCEPTION_CODE_STORE_NOT_EXIST);
                //查询教练状态
                if (!Objects.equals(businessStore.getStoreState(), BusinessState.STORE_STATE_NORMAL))
                    throw new UserException(UserExceptionCode.USER_EXCEPTION_CODE_STORE_NOT_NORMAL);
                Double score = redisUtil.zScore(UserRedisKeyUtil.followStoreRank(), storeId);
                FollowStoreRankDto followStoreRankDto = BeanUtil.toBean(businessStore, FollowStoreRankDto.class);
                followStoreRankDto.setStoreId(Integer.parseInt(storeId));
                followStoreRankDto.setScore(score);
                followStoreRankDtos.add(followStoreRankDto);
            }
        }
        return followStoreRankDtos;
    }

    /**
     * 场馆分页查看被关注的用户信息
     * @param param 查询被关注用户参数
     * @return PageInfo
     */
    @Override
    public PageInfo listStoreFollowedUsers(ListFollowedUsersParam param) {
        Result<BusinessStore> result = businessClient.selByStoreId(param.getId());
        if (result.getCode()!=200) throw new UserException(result.getCode(),result.getMessage());
        //创建分页对象
        Page<UserInfoDto> page = new Page<>(param.getPageNum()==null||param.getPageNum().equals("")?1:param.getPageNum(),
                param.getPageSize()==null||param.getPageSize().equals("")?10:param.getPageSize());
        QueryWrapper<UserInfo> wrapper = new QueryWrapper<>();
        if (ObjectUtil.isNotEmpty(param.getUserName()))
            wrapper.like("user_name",param.getUserName());
        if (ObjectUtil.isNotEmpty(param.getUserPhone()))
            wrapper.like("user_phone",param.getUserPhone());
        wrapper.eq("user_state",UserState.USER_ACCOUNT_STATE_NORMAL);
        List<UserInfo> userInfos = userInfoMapper.selectList(wrapper);
        List<UserInfoDto> userInfoDtos = new ArrayList<>();
        for (UserInfo userInfo : userInfos) {
            if (redisUtil.hasKey(UserRedisKeyUtil.followStore(userInfo.getUserId()))){
                UserInfoDto userInfoDto = BeanUtil.toBean(userInfo, UserInfoDto.class);
                userInfoDtos.add(userInfoDto);
            }
        }
        page.setRecords(userInfoDtos);
        PageInfo pageInfo = new PageInfo();
        pageInfo.setRecords(page.getRecords());
        pageInfo.setPages(page.getPages());
        return pageInfo;
    }

    /**
     * 用户注销账号
     * @param userId 用户id
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void userDelete(Integer userId) {
        //查询账号
        UserInfo userInfo = userInfoMapper.selectById(userId);
        if (userInfo==null) throw new UserException(UserExceptionCode.USER_EXCEPTION_CODE_COACH_NOT_EXIST);
        //判断用户账号是否禁用，已禁用无法登录进行注销操作
        if (Objects.equals(userInfo.getUserState(), UserState.USER_ACCOUNT_STATE_DISABLE))
            throw new UserException(UserExceptionCode.USER_EXCEPTION_CODE_STATE_NOT_NORMAL);
        //判断账号是否已经删除
        if (Objects.equals(userInfo.getUserState(), UserState.USER_ACCOUNT_STATE_DELETE))
            throw new UserException(UserExceptionCode.USER_EXCEPTION_CODE_STATE_NOT_NORMAL);
        //删除账号
        userInfo.setUserState(UserState.USER_ACCOUNT_STATE_DELETE);
        userInfoMapper.updateById(userInfo);
    }

    /**
     * 通过用户id集合查询用户信息
     * @param param 参数
     * @return PageInfo
     */
    @Override
    public PageInfo selectUsersByIds(SelectUsersByIdsParam param) {
        if (param.getCurrent()==null||param.getCurrent().equals(""))
            param.setCurrent(1);
        if (param.getSize()==null||param.getSize().equals(""))
            param.setSize(10);
        //创建返回对象
        List<UserByIdsDto> userByIdsDtos = new ArrayList<>();
        //通过ids分别查询对象
        for (Integer userId : param.getUserIds()) {
            UserInfo userInfo = userInfoMapper.selectById(userId);
            if (userInfo==null) throw new UserException(UserExceptionCode.USER_EXCEPTION_CODE_COACH_NOT_EXIST);
            if (userInfo.getUserState()!=UserState.USER_ACCOUNT_STATE_NORMAL)
                throw new UserException(UserExceptionCode.USER_EXCEPTION_CODE_STATE_NOT_NORMAL);
            UserByIdsDto userByIdsDto = BeanUtil.toBean(userInfo, UserByIdsDto.class);
            userByIdsDtos.add(userByIdsDto);
        }
        //创建分页对象
        PageInfo pageInfo = new PageInfo();
        //判断条件查询
        List<UserByIdsDto> dtos = new ArrayList<>();
        if (ObjectUtil.isNotEmpty(param.getRealName())){
            dtos = userByIdsDtos.stream().filter(o -> o.getRealName().contains(param.getRealName())).collect(Collectors.toList());
            long total = dtos.size();
            Long pages = (total%param.getSize()==0)?(total/param.getSize()):(total/param.getSize())+1;
            pageInfo.setCurrent(param.getCurrent());
            pageInfo.setSize(param.getSize());
            pageInfo.setTotal(total);
            //获取当前页数据
            int startIndex = (int) ((pageInfo.getCurrent()-1)*pageInfo.getSize());
            int endIndex = (int) Math.min(startIndex + pageInfo.getSize(), pageInfo.getTotal());
            List<UserByIdsDto> list = dtos.subList(startIndex, endIndex);
            pageInfo.setPages(pages);
            pageInfo.setRecords(list);
            return pageInfo;
        }
        if (ObjectUtil.isNotEmpty(param.getUserPhone())){
            dtos = userByIdsDtos.stream().filter(o -> o.getUserPhone().contains(param.getUserPhone())).collect(Collectors.toList());
            long total = dtos.size();
            Long pages = (total%param.getSize()==0)?(total/param.getSize()):(total/param.getSize())+1;
            pageInfo.setCurrent(param.getCurrent());
            pageInfo.setSize(param.getSize());
            pageInfo.setTotal(total);
            //获取当前页数据
            int startIndex = (int) ((pageInfo.getCurrent()-1)*pageInfo.getSize());
            int endIndex = (int) Math.min(startIndex + pageInfo.getSize(), pageInfo.getTotal());
            List<UserByIdsDto> list = dtos.subList(startIndex, endIndex);
            pageInfo.setPages(pages);
            pageInfo.setRecords(list);
            return pageInfo;
        }
        long total = userByIdsDtos.size();
        Long pages = (total%param.getSize()==0)?(total/param.getSize()):(total/param.getSize())+1;
        pageInfo.setCurrent(param.getCurrent());
        pageInfo.setSize(param.getSize());
        pageInfo.setTotal(total);
        //获取当前页数据
        int startIndex = (int) ((pageInfo.getCurrent()-1)*pageInfo.getSize());
        int endIndex = (int) Math.min(startIndex + pageInfo.getSize(), pageInfo.getTotal());
        List<UserByIdsDto> list = userByIdsDtos.subList(startIndex, endIndex);
        pageInfo.setPages(pages);
        pageInfo.setRecords(list);
        return pageInfo;
    }
}
